Project
Contents
Project¶
In diesem Projekt wird eine Explorative Datenanalyse von Automobilen durchgeführt.
Als Datengrundlage dient die Webseite Autoscout24 (https://www.autoscout24.de/), eine Online-Plattform zum Kauf und Verkauf von Neu- und Gebrauchtwagen.
Ziel ist es, die Daten verschiedener Fahrzeuge zu vergleichen und daraus mögliche Schlüsse auf Korrelation der Daten zu ziehen. So soll beispielsweise analysiert werden, welche Fahrzeugeigentschaften einen Einfluss auf dessen Verkaufspreis haben.
Eine weitere interessante Fragestellung ist der Zusammenhang zwischen Kraftstoffart und Verbrauchsdaten.
Die im Projekt getätigten Analysen und Vergleiche verschiedener Fahrzeuge sollen zudem als Kaufentscheidung eines Fahrzeugs dienen.
Import¶
Zunächst wreden alle für dieses Projekt benötigten Packages und Bibliothen importiert.
#Basics
import pandas as pd
import numpy as np
#Webcrawling
#pip install beautifulsoup4
from bs4 import BeautifulSoup
import requests
#Deactivate warnings
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
#Data visualization
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objs as go
import plotly.io as pio
import plotly.figure_factory as ff
import plotly.offline as pyo
# Set notebook mode to work in offline
pyo.init_notebook_mode()
#np.set_printoptions(precision=6)
#np.set_printoptions(suppress=True)
import seaborn as sns
#Geo visualization
import folium
#!pip install geopy
from geopy.geocoders import Nominatim
Webcrawling & Creation of Dataframe¶
Als Erstes ist es notwendig, die Fahrzeugdaten von der Webseite Autoscout24 zu crawlen und in einem Dataframe zu speichern.
Für das Crawlen der Daten wird die Methode extractPageCarDF definiert. Dieser muss beim Aufruf die Variable URL mitgegeben werden. Dabei handelt es sich um den Link zu einem Autoscout24 Suchergebnis, welches stehts 20 Autos beinhaltet (sofern sie den Suchkriterien entsprechen).
Jedes Auto wird dabei vom HTML Element Article umschlossen. Daher wird eine Schleife implementiert, welche für jedes Auto im Suchergebnis die nachfolgenden Daten aus den dazugehörigen HTML Elementen der Webseite extrahiert:
Titel
Fahrzeugversion
Untertitel
Preis
Leasingpreis
Fahrzeugstandort
Falls eines der HTML-Elemente nicht gefunden werden kann, wird jede der Anweisungen durch einen try except Block umschlossen. Falls kein Eintrag gefunden wird, wird der jeweilige Wert mit einem NULL-Wert belegt.
Eine Besonderheit ist zudem der Preis. Wird das Element ListItem_pricerow gefunden, handelt es sich um einen Verkaufspreis und kein Leasingangebot. Leasing wird daher False gesetzt. Wird dieses Element nicht gefunden, sondern LeasingPrice_price handelt es sich um ein Leasing Angebot.
Diese Daten werden dem Dataframe pageCarDF hinzugefügt.
In einer weiteren Schleife werden die folgenden Fahrzeugdetaildaten aus dem HTML Div Container VehicleDetailTable abgezogen:
Es wird zunächst ein leeres Dataframe initialisiert.
In einer Schleife wird für jedes Fahrzeug die leere Liste VehicleDetailList erzeugt und dieser in einer inneren Schleife jedes Element der VehicleDetailTable hinzugefügt.
Die Liste wird anschließend dem VehicleDetailDF hinzugefügt. Hierfür ist ein try except notwendig. Leasing Fahrzeuge haben ein zweites Element namens VehicleDetailTable, welches allerdings nur 3 Einträge zum Themengebiet Leasing hat. Der Versuch diese Listen mit 3 Einträgen dem VehicleDetailDF hinzuzufügen, läuft aufgrund nicht passender Längen auf Fehler. Dieser Fehler wird im except Block bewusst mit einem Continue abgefangen. Die Leasing VehicleDetailLists werden nicht weiter benötigt und fallen somit raus.
Nun liegt das pageCarDF und das VehicleDetailDF vor, welche beide je Fahrzeug eine Zeile beinhalten.
Die beiden Dataframes werden mithilfe der merge-Methode über den Index gejoined.
Die Methode gibt das Dataframe pageCarDF als return value zurück. Diese beinhaltet alle relevanten Daten von den Fahrzeugen einer Suchergebnisseite (in der Regel 20 Fahrzeuge).
def extractPageCarDF(URL):
soup=BeautifulSoup(requests.get(URL).text,"html.parser")
pageCarDF=pd.DataFrame()
for car in soup.findAll("article"):
data = car.find("div", {"class": lambda L: L and L.startswith("ListItem_wrapper")})
try:
header = data.find("h2").text
except:
header = np.NaN
try:
version = data.find("span", {"class": lambda L: L and L.startswith("ListItem_version")}).text
except:
version = np.NaN
try:
subtitle = data.find("span", {"class": lambda L: L and L.startswith("ListItem_subtitle")}).text
except:
subtitle = np.NaN
try:
#Versuch Preis Element zu finden
price = data.find("div", {"class": lambda L: L and L.startswith("ListItem_pricerow")}).text
leasing = False
except:
#wenn oberes Element nicht gefunden werden kann, handelt es sich um einen Leasing Wagen, mit dem nachfolgenden HTML Element
price = data.find("span", {"class": lambda L: L and L.startswith("LeasingPrice_price")}).text
leasing = True
try:
location = car.find("span", {"style": lambda L: L and L.startswith("grid-area:address")}).text
except:
location = np.NaN
#Daten dem pageCarDF hinzufügen
pageCarDF = pageCarDF.append({"Titel":header, "Version":version, "Untertitel":subtitle, "Preis":price, "Leasing":leasing, "Standort":location}, ignore_index=True)
#VehicleDetailTable
VehicleDetailDF = pd.DataFrame()
for car in soup.findAll("div" , {"class":"VehicleDetailTable_container__mUUbY"}):
VehicleDetailList = []
for c in car:
VehicleDetailList.append(c.text)
try:
VehicleDetailDF = VehicleDetailDF.append({"km":VehicleDetailList[0], "Erstzulassung":VehicleDetailList[1], "PS":VehicleDetailList[2], "Zustand":VehicleDetailList[3], "Fahrzeughalter":VehicleDetailList[4], "Getriebe":VehicleDetailList[5], "Kraftstoff": VehicleDetailList[6], "Verbrauch_l_pro_100km":VehicleDetailList[7], "Emissionen_g_pro_km":VehicleDetailList[8]}, ignore_index=True)
except:
continue #VehicleDetailLists mit Länge 3 sind extra VehicleDetailTables, die nur bei Leasing Wagen vorkommen. Diese sollen nicht übernommen werden, daher Continue
#Join pageCarDF und VehicleDetailDF
pageCarDF = pd.merge(pageCarDF, VehicleDetailDF, left_index=True, right_index=True)
return pageCarDF
Die Methode extractPageCarDF gilt es nun mit den passenden Paramentern aufzurufen.
Es werden zunächst zwei leere Dataframes initialisiert.
Die Suche auf Autoscout24 wurde zunächst komplett ohne Filter aufgerufen. Pro Suchergebnis gibt die Webseite ingesamt 20 Suchergebnisseiten mit jeweils 20 Fahrzeugen aus. Somit können mit einer Suche maximal 20 * 20 = 400 Autos von der Webseite gecrawlt werden.
Da für die Analysen im Projekt mehr als 400 Datensätze gewünscht sind, wird ein Filter “Erstzulassung bis” gesetzt. Die Jahreszahlen werden in der Liste fregtoList von 1990 bis 2022 in 2er Schritten gewählt.
In einer Schleife wird zunächst der Filter auf die jeweilige Jahreszahl gesetzt. In einer inneren Schleife werden jeweils die 20 Seiten des Suchergebnisses gecrawlt.
Dafür wird zunächst die URL aus “Erstzulassung bis” fregto= und Suchergebnisseite page erstellt. Die URL wird an die Methode extractPageCarDF übergeben und diese ausgeführt.
Das resultierende Dataframe pageCarDF mit 20 Einträgen wird dem Dataframe AutoDFraw angehängt. Anschließend wird die Methode für die nächste Seite um Suchergebnis ausgeführt und das Ergebnis wieder AutoDFraw hinzufügt.
Dataframe pageCarDF wird somit bei jeder Ausführung der Methode extractPageCarDF neu erstellt, während Dataframe AutoDFraw immer weiter wächst.
Die Methode wird für jeden Filter “Erstzulassung bis” für 20 Suchergebnisseiten ausgeführt, sodass das Dataframe AutoDFraw am Ende über 6000 Einträge enhält.
AutoDFraw=pd.DataFrame()
pageCarDF=pd.DataFrame()
baselink = "https://www.autoscout24.de/lst?fregto="
fregtoList = list(range(1995, 2022, 1))
#fregfromList = list(range(1991, 2021, 2))
#fregfromList.insert(0, 1900)
for freg in fregtoList:
for page in range(20):
URL = baselink + str(freg) + "&page=" + str(page)
pageCarDF = extractPageCarDF(URL)
AutoDFraw=pd.concat([AutoDFraw, pageCarDF],axis=0, ignore_index=True)
AutoDFraw
| Titel | Version | Untertitel | Preis | Leasing | Standort | km | Erstzulassung | PS | Zustand | Fahrzeughalter | Getriebe | Kraftstoff | Verbrauch_l_pro_100km | Emissionen_g_pro_km | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Lincoln Town Car | Signature Series Prins Gasanlage Voll | NaN | € 4.900,-Keine Angabe | 0.0 | Funda Aktas • DE-12107 Berlin | 277.711 km | 10/1993 | 157 kW (213 PS) | Gebraucht | 2 Fahrzeughalter | Automatik | Autogas (LPG) | - (l/100 km) | - (g/km) |
| 1 | Mercedes-Benz 200 | HECKFLOSSE W110 DAIMLER BENZ | NaN | € 14.870,-Keine Angabe | 0.0 | Norbert Lindemann • DE-12305 Berlin | 39.526 km | 12/1965 | 70 kW (95 PS) | Gebraucht | - (Fahrzeughalter) | Schaltgetriebe | Benzin | - (l/100 km) | - (g/km) |
| 2 | Land Rover Discovery | 2.5 Base TDI | NaN | € 3.990,- | 0.0 | Contáctanos en: • ES-17800 OLOT | 214.446 km | 05/1992 | 82 kW (111 PS) | Gebraucht | - (Fahrzeughalter) | Schaltgetriebe | Diesel | 8,9 l/100 km (komb.) | - (g/km) |
| 3 | MG MGB | GT aus 1. Hand, original 53.000 KM | NaN | € 25.900,-Keine Angabe | 0.0 | Britta Mirbach • DE-22297 Hamburg | 53.372 km | 07/1975 | 98 kW (133 PS) | Gebraucht | 1 Fahrzeughalter | Schaltgetriebe | - (Kraftstoff) | - (l/100 km) | - (g/km) |
| 4 | Mercedes-Benz | 220 S Ponton W180 Oldtimer H-Kennz. fahrbereit | NaN | € 22.950,-Keine Angabe | 0.0 | DE-92318 Neumarkt | 54.900 km | 01/1957 | 74 kW (101 PS) | Gebraucht | - (Fahrzeughalter) | Schaltgetriebe | Benzin | - (l/100 km) | 0 g/km (komb.) |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 10268 | Skoda Superb | Combi 2.0TDI Active AHK Sitzheizung | Anhängerkupplung, Armlehne, Sitzheizung, Dachr... | € 22.840,-Sehr guter Preis | 0.0 | Ihr Verkaufsteam Nürnberg • DE-90441 Nürnberg | 56.800 km | 11/2019 | 110 kW (150 PS) | Gebraucht | 1 Fahrzeughalter | Schaltgetriebe | Diesel | - (l/100 km) | - (g/km) |
| 10269 | BMW Z4 | sDrive20i Sport Steptronic Xenon Leder SHZ | Sportfahrwerk, Sportpaket, Sportsitze, Einpark... | € 22.990,-Guter Preis | 0.0 | Carlo Jesse • DE-49477 Ibbenbüren | 85.300 km | 10/2012 | 135 kW (184 PS) | Gebraucht | 2 Fahrzeughalter | Automatik | Benzin | 6,8 l/100 km (komb.) | 161 g/km (komb.) |
| 10270 | Audi A3 | 2,0 Cabriolet Ambition S-line+Sitzheizung+Tagf... | Mitsubishi & Honda Vertragspartner seit 40 Jahren | € 14.880,-Sehr guter Preis | 0.0 | - - • DE-41061 Mönchengladbach | 68.335 km | 04/2012 | 147 kW (200 PS) | Gebraucht | 3 Fahrzeughalter | Schaltgetriebe | Benzin | 7,2 l/100 km (komb.) | 171 g/km (komb.) |
| 10271 | Honda CR-V | 2,0 i-VTEC Comfort Leder+Allrad+Multif.Lenkrad... | Allrad, Zentralverriegelung, Lederlenkrad, Kat... | € 14.280,-Sehr guter Preis | 0.0 | - - • DE-41747 Viersen | 56.105 km | 05/2012 | 110 kW (150 PS) | Gebraucht | 1 Fahrzeughalter | Automatik | Benzin | 8,4 l/100 km (komb.) | 195 g/km (komb.) |
| 10272 | Hyundai i30 | 1.6 Turbo GDI Turbo | Zentralverriegelung, Tempomat, Lichtsensor, Tr... | € 13.290,-Sehr guter Preis | 0.0 | Kevin Schuster • DE-85276 Pfaffenhofen | 103.882 km | 02/2016 | 137 kW (186 PS) | Gebraucht | 2 Fahrzeughalter | Schaltgetriebe | Benzin | 7,3 l/100 km (komb.) | 173 g/km (komb.) |
10273 rows × 15 columns
Raw Data Transformation¶
Die Fahrzeugdaten von Autoscout24 wurden erfolgreich abgezogen und in einem Dataframe gespeichert. Allerdings entsprechen viele Spalten noch nicht dem gewünschten Format, da beispielsweise Sonderzeichen enthalten sind oder numerische Werte nicht also solche erkannt werden.
Aus diesem Grund muss das Dataframe nun so bearbeitet werden, dass alle Spalten in einer für die Explorative Datenanalyse sinnvollen Struktur vorliegen.
Zunächst werden die Rohdaten in einer Excel Tabelle gespeichert. So kann jederzeit auf die Rohdaten zurückgegriffen werden und auch das Ergebnis der Datentransformation nachvollzogen werden.
AutoDFraw.to_excel("AutoDF_vor_Replace.xlsx")
Anschließend wird das Dataframe AutoDF mit den Rohdaten initialisiert.
AutoDF= AutoDFraw
Eine für die Datenanalyse interessante Information ist die Automarke. Diese ist im Titel der Anzeige als erstes Wort enthalten. Daher wird zur Bestimmung der Automarke das erste Wort der Spalte Titel extrahiert und in einer neuen Spalte Marke gespeichert.
AutoDF['Marke'] = AutoDF['Titel'].str.split('\s+').str[0]
Im nächsten Schritt wird das Dataframe um störende oder überflüssige Character bereinigt. Dazu gehören störende Satzzeichen, Währungen, Strings, etc.
Da in einzelnen Fällen Leasingpreise noch hinter Kaufpreisen angezeigt werden, müssen zuerst alle Zeichen hinter dem ersten Kaufpreis entfernt werden. Dann werden in der nächsten Codezeile alle weiteren nicht numerischen Zeichen entfernt.
AutoDF['Preis'] = AutoDF['Preis'].replace('(,-).*', '',regex=True)
AutoDF['Preis'] = AutoDF['Preis'].str.replace(r'[^0-9]+', '')
Die nachfolgenden Spalten enthalten im Datensatz noch Einheiten. Diese sind bereits im Spaltentitel vorhanden und werden somit aus den Datensätzen entfernt, sodass nur noch numerische Werte verbleiben.
AutoDF['km'] = AutoDF['km'].replace(r'[^0-9]+', '',regex=True)
AutoDF['Fahrzeughalter'] = AutoDF['Fahrzeughalter'].replace(r'[^0-9]+', '',regex=True)
AutoDF['Verbrauch_l_pro_100km'] = AutoDF['Verbrauch_l_pro_100km'].replace(['\(l/100 km\)', 'l/100 km','\(komb.\)'], '',regex=True)
AutoDF['Emissionen_g_pro_km'] = AutoDF['Emissionen_g_pro_km'].replace(r'[^0-9]+', '',regex=True)
Der Monat der Erstzulassung wird entfernt sowie alle übrigbleibenden nicht numerischen Zeichen.
AutoDF['Erstzulassung'] = AutoDF['Erstzulassung'].replace('.*/', '',regex=True)
AutoDF['Erstzulassung'] = AutoDF['Erstzulassung'].replace(r'[^0-9]+', '',regex=True)
Bei der PS-Angabe muss zuerst der Wert in kW entfernt werden, danach alle weiteren nicht numerischen Zeichen.
AutoDF['PS'] = AutoDF['PS'].replace(['.*kW','\(','PS\)'], '',regex=True)
AutoDF['PS'] = AutoDF['PS'].replace(r'[^0-9]+', '',regex=True)
Oli check hier den Text nochmal bitte¶
Bei der Bereinigung fehlender Werte tritt das Problem auf, dass bei den Attributen Verbrauch und Emissionen fehlende Werte bei Elektroautos = 0 bedeuten, bei nicht Elektroautos jedoch tatsächlich fehlende Werte.
Da bei Verbrauch und Emissionen von Nutzern teilweise keine Werte angegeben werden, werden alle fehlenden Werte durch NULL ersetzt, damit diese später ausgewertet werden können.
AutoDF['Verbrauch_l_pro_100km'] = AutoDF['Verbrauch_l_pro_100km'].replace(['-','','0'], np.NaN,regex=True)
AutoDF['Emissionen_g_pro_km'] = AutoDF['Emissionen_g_pro_km'].replace(['-','','0'], np.NaN,regex=True)
AutoDF['Fahrzeughalter'] = AutoDF['Fahrzeughalter'].replace(['-',''], np.NaN,regex=True)
AutoDF['Erstzulassung'] = AutoDF['Erstzulassung'].replace('', np.NaN,regex=True)
AutoDF['km'] = AutoDF['km'].replace('', np.NaN,regex=True)
AutoDF['PS'] = AutoDF['PS'].replace('', np.NaN,regex=True)
# da keine Angabe bei Verbrauch und Emissionen bei Elektroautos korrekt sein kann, wird der Wert wieder durch 0 ersetzt
AutoDF.loc[AutoDF.Kraftstoff == 'Elektro', 'Verbrauch_l_pro_100km'] = 0
AutoDF.loc[AutoDF.Kraftstoff == 'Elektro', 'Emissionen_g_pro_km'] = 0
AutoDF
| Titel | Version | Untertitel | Preis | Leasing | Standort | km | Erstzulassung | PS | Zustand | Fahrzeughalter | Getriebe | Kraftstoff | Verbrauch_l_pro_100km | Emissionen_g_pro_km | Marke | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Lincoln Town Car | Signature Series Prins Gasanlage Voll | NaN | 4900 | 0.0 | Funda Aktas • DE-12107 Berlin | 277711 | 1993 | 213 | Gebraucht | 2 | Automatik | Autogas (LPG) | NaN | NaN | Lincoln |
| 1 | Mercedes-Benz 200 | HECKFLOSSE W110 DAIMLER BENZ | NaN | 14870 | 0.0 | Norbert Lindemann • DE-12305 Berlin | 39526 | 1965 | 95 | Gebraucht | NaN | Schaltgetriebe | Benzin | NaN | NaN | Mercedes-Benz |
| 2 | Land Rover Discovery | 2.5 Base TDI | NaN | 3990 | 0.0 | Contáctanos en: • ES-17800 OLOT | 214446 | 1992 | 111 | Gebraucht | NaN | Schaltgetriebe | Diesel | 8,9 | NaN | Land |
| 3 | MG MGB | GT aus 1. Hand, original 53.000 KM | NaN | 25900 | 0.0 | Britta Mirbach • DE-22297 Hamburg | 53372 | 1975 | 133 | Gebraucht | 1 | Schaltgetriebe | - (Kraftstoff) | NaN | NaN | MG |
| 4 | Mercedes-Benz | 220 S Ponton W180 Oldtimer H-Kennz. fahrbereit | NaN | 22950 | 0.0 | DE-92318 Neumarkt | 54900 | 1957 | 101 | Gebraucht | NaN | Schaltgetriebe | Benzin | NaN | NaN | Mercedes-Benz |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 10268 | Skoda Superb | Combi 2.0TDI Active AHK Sitzheizung | Anhängerkupplung, Armlehne, Sitzheizung, Dachr... | 22840 | 0.0 | Ihr Verkaufsteam Nürnberg • DE-90441 Nürnberg | 56800 | 2019 | 150 | Gebraucht | 1 | Schaltgetriebe | Diesel | NaN | NaN | Skoda |
| 10269 | BMW Z4 | sDrive20i Sport Steptronic Xenon Leder SHZ | Sportfahrwerk, Sportpaket, Sportsitze, Einpark... | 22990 | 0.0 | Carlo Jesse • DE-49477 Ibbenbüren | 85300 | 2012 | 184 | Gebraucht | 2 | Automatik | Benzin | 6,8 | 161 | BMW |
| 10270 | Audi A3 | 2,0 Cabriolet Ambition S-line+Sitzheizung+Tagf... | Mitsubishi & Honda Vertragspartner seit 40 Jahren | 14880 | 0.0 | - - • DE-41061 Mönchengladbach | 68335 | 2012 | 200 | Gebraucht | 3 | Schaltgetriebe | Benzin | 7,2 | 171 | Audi |
| 10271 | Honda CR-V | 2,0 i-VTEC Comfort Leder+Allrad+Multif.Lenkrad... | Allrad, Zentralverriegelung, Lederlenkrad, Kat... | 14280 | 0.0 | - - • DE-41747 Viersen | 56105 | 2012 | 150 | Gebraucht | 1 | Automatik | Benzin | 8,4 | 195 | Honda |
| 10272 | Hyundai i30 | 1.6 Turbo GDI Turbo | Zentralverriegelung, Tempomat, Lichtsensor, Tr... | 13290 | 0.0 | Kevin Schuster • DE-85276 Pfaffenhofen | 103882 | 2016 | 186 | Gebraucht | 2 | Schaltgetriebe | Benzin | 7,3 | 173 | Hyundai |
10273 rows × 16 columns
In der spalte Verbrauch_l_pro_100km wird das Komma zur Dezimaltrennung durch einen Punkt ersetzt.
AutoDF['Verbrauch_l_pro_100km'] = AutoDF['Verbrauch_l_pro_100km'].replace(',', '.',regex=True)
Die Spalte Standort wird aufgeteilt in die Spalten PLZ, Stadt und Land.
Das letzte Wort der Spalte Standort ist immer der Stadtname.
Die Postleitzahl kann extrahiert werden, indem alle nicht numerischen Zeichen entfernt werden.
Land und PLZ werden durch einen Bindestrich getrennt. Daher kann der Bindestrich zum Split verwendet werden, wobei der erste Teil weiterverwendet wird. Dieser wird nochmal mit durch Leerzeichen getrennt und hiervon der letzte Teil als Land gespeichert.
#Stadtname
AutoDF['Stadt'] = AutoDF['Standort'].str.split(' ').str[-1]
#PLZ
AutoDF['PLZ'] = AutoDF['Standort'].replace(r'[^0-9]+', '',regex=True)
#Land
AutoDF['Land'] = AutoDF['Standort'].str.split('-').str[-2].str.split(' ').str[-1]
AutoDF
| Titel | Version | Untertitel | Preis | Leasing | Standort | km | Erstzulassung | PS | Zustand | Fahrzeughalter | Getriebe | Kraftstoff | Verbrauch_l_pro_100km | Emissionen_g_pro_km | Marke | Stadt | PLZ | Land | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Lincoln Town Car | Signature Series Prins Gasanlage Voll | NaN | 4900 | 0.0 | Funda Aktas • DE-12107 Berlin | 277711 | 1993 | 213 | Gebraucht | 2 | Automatik | Autogas (LPG) | NaN | NaN | Lincoln | Berlin | 12107 | DE |
| 1 | Mercedes-Benz 200 | HECKFLOSSE W110 DAIMLER BENZ | NaN | 14870 | 0.0 | Norbert Lindemann • DE-12305 Berlin | 39526 | 1965 | 95 | Gebraucht | NaN | Schaltgetriebe | Benzin | NaN | NaN | Mercedes-Benz | Berlin | 12305 | DE |
| 2 | Land Rover Discovery | 2.5 Base TDI | NaN | 3990 | 0.0 | Contáctanos en: • ES-17800 OLOT | 214446 | 1992 | 111 | Gebraucht | NaN | Schaltgetriebe | Diesel | 8.9 | NaN | Land | OLOT | 17800 | ES |
| 3 | MG MGB | GT aus 1. Hand, original 53.000 KM | NaN | 25900 | 0.0 | Britta Mirbach • DE-22297 Hamburg | 53372 | 1975 | 133 | Gebraucht | 1 | Schaltgetriebe | - (Kraftstoff) | NaN | NaN | MG | Hamburg | 22297 | DE |
| 4 | Mercedes-Benz | 220 S Ponton W180 Oldtimer H-Kennz. fahrbereit | NaN | 22950 | 0.0 | DE-92318 Neumarkt | 54900 | 1957 | 101 | Gebraucht | NaN | Schaltgetriebe | Benzin | NaN | NaN | Mercedes-Benz | Neumarkt | 92318 | DE |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 10268 | Skoda Superb | Combi 2.0TDI Active AHK Sitzheizung | Anhängerkupplung, Armlehne, Sitzheizung, Dachr... | 22840 | 0.0 | Ihr Verkaufsteam Nürnberg • DE-90441 Nürnberg | 56800 | 2019 | 150 | Gebraucht | 1 | Schaltgetriebe | Diesel | NaN | NaN | Skoda | Nürnberg | 90441 | DE |
| 10269 | BMW Z4 | sDrive20i Sport Steptronic Xenon Leder SHZ | Sportfahrwerk, Sportpaket, Sportsitze, Einpark... | 22990 | 0.0 | Carlo Jesse • DE-49477 Ibbenbüren | 85300 | 2012 | 184 | Gebraucht | 2 | Automatik | Benzin | 6.8 | 161 | BMW | Ibbenbüren | 49477 | DE |
| 10270 | Audi A3 | 2,0 Cabriolet Ambition S-line+Sitzheizung+Tagf... | Mitsubishi & Honda Vertragspartner seit 40 Jahren | 14880 | 0.0 | - - • DE-41061 Mönchengladbach | 68335 | 2012 | 200 | Gebraucht | 3 | Schaltgetriebe | Benzin | 7.2 | 171 | Audi | Mönchengladbach | 41061 | DE |
| 10271 | Honda CR-V | 2,0 i-VTEC Comfort Leder+Allrad+Multif.Lenkrad... | Allrad, Zentralverriegelung, Lederlenkrad, Kat... | 14280 | 0.0 | - - • DE-41747 Viersen | 56105 | 2012 | 150 | Gebraucht | 1 | Automatik | Benzin | 8.4 | 195 | Honda | Viersen | 41747 | DE |
| 10272 | Hyundai i30 | 1.6 Turbo GDI Turbo | Zentralverriegelung, Tempomat, Lichtsensor, Tr... | 13290 | 0.0 | Kevin Schuster • DE-85276 Pfaffenhofen | 103882 | 2016 | 186 | Gebraucht | 2 | Schaltgetriebe | Benzin | 7.3 | 173 | Hyundai | Pfaffenhofen | 85276 | DE |
10273 rows × 19 columns
Spalte Standort wird nicht weiter benötigt und kann entfernt werden.
AutoDF = AutoDF.drop('Standort', axis=1)
In der Spalte Untertitel werden Ausstattungsmerkmale des Fahrzeugs aufgezählt. Einige ausgewählte Austattungsmerkmale werden als extra Spalten in das Dataframe aufgenommen.
Dafür wird folgende Annahme getroffen:
Ein Fahrzeug besitzt eine bestimmte Ausstattung, wenn diese in Spalte Untertitel erwähnt wird. Wird diese dort nicht erwähnt, besitzt ein Fahrzeug diese Ausstattung nicht.
Dies wird mithilfe der Methode str.contains geprüft.
AutoDF['Alufelgen']= AutoDF['Untertitel'].str.contains("Alufelgen")
AutoDF['Sitzheizung']= AutoDF['Untertitel'].str.contains("Sitzheizung")
AutoDF['Klimaanlage']= (AutoDF['Untertitel'].str.contains("Klimaanlage")) | (AutoDF['Untertitel'].str.contains("Klimaautomatik"))
AutoDF['Einparkhilfe']= AutoDF['Untertitel'].str.contains("Einparkhilfe ")
AutoDF['Navigationssystem']= AutoDF['Untertitel'].str.contains("Navigationssystem")
AutoDF
| Titel | Version | Untertitel | Preis | Leasing | km | Erstzulassung | PS | Zustand | Fahrzeughalter | ... | Emissionen_g_pro_km | Marke | Stadt | PLZ | Land | Alufelgen | Sitzheizung | Klimaanlage | Einparkhilfe | Navigationssystem | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Lincoln Town Car | Signature Series Prins Gasanlage Voll | NaN | 4900 | 0.0 | 277711 | 1993 | 213 | Gebraucht | 2 | ... | NaN | Lincoln | Berlin | 12107 | DE | NaN | NaN | False | NaN | NaN |
| 1 | Mercedes-Benz 200 | HECKFLOSSE W110 DAIMLER BENZ | NaN | 14870 | 0.0 | 39526 | 1965 | 95 | Gebraucht | NaN | ... | NaN | Mercedes-Benz | Berlin | 12305 | DE | NaN | NaN | False | NaN | NaN |
| 2 | Land Rover Discovery | 2.5 Base TDI | NaN | 3990 | 0.0 | 214446 | 1992 | 111 | Gebraucht | NaN | ... | NaN | Land | OLOT | 17800 | ES | NaN | NaN | False | NaN | NaN |
| 3 | MG MGB | GT aus 1. Hand, original 53.000 KM | NaN | 25900 | 0.0 | 53372 | 1975 | 133 | Gebraucht | 1 | ... | NaN | MG | Hamburg | 22297 | DE | NaN | NaN | False | NaN | NaN |
| 4 | Mercedes-Benz | 220 S Ponton W180 Oldtimer H-Kennz. fahrbereit | NaN | 22950 | 0.0 | 54900 | 1957 | 101 | Gebraucht | NaN | ... | NaN | Mercedes-Benz | Neumarkt | 92318 | DE | NaN | NaN | False | NaN | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 10268 | Skoda Superb | Combi 2.0TDI Active AHK Sitzheizung | Anhängerkupplung, Armlehne, Sitzheizung, Dachr... | 22840 | 0.0 | 56800 | 2019 | 150 | Gebraucht | 1 | ... | NaN | Skoda | Nürnberg | 90441 | DE | False | True | False | False | False |
| 10269 | BMW Z4 | sDrive20i Sport Steptronic Xenon Leder SHZ | Sportfahrwerk, Sportpaket, Sportsitze, Einpark... | 22990 | 0.0 | 85300 | 2012 | 184 | Gebraucht | 2 | ... | 161 | BMW | Ibbenbüren | 49477 | DE | False | True | False | True | False |
| 10270 | Audi A3 | 2,0 Cabriolet Ambition S-line+Sitzheizung+Tagf... | Mitsubishi & Honda Vertragspartner seit 40 Jahren | 14880 | 0.0 | 68335 | 2012 | 200 | Gebraucht | 3 | ... | 171 | Audi | Mönchengladbach | 41061 | DE | False | False | False | False | False |
| 10271 | Honda CR-V | 2,0 i-VTEC Comfort Leder+Allrad+Multif.Lenkrad... | Allrad, Zentralverriegelung, Lederlenkrad, Kat... | 14280 | 0.0 | 56105 | 2012 | 150 | Gebraucht | 1 | ... | 195 | Honda | Viersen | 41747 | DE | True | False | False | False | False |
| 10272 | Hyundai i30 | 1.6 Turbo GDI Turbo | Zentralverriegelung, Tempomat, Lichtsensor, Tr... | 13290 | 0.0 | 103882 | 2016 | 186 | Gebraucht | 2 | ... | 173 | Hyundai | Pfaffenhofen | 85276 | DE | False | True | True | False | False |
10273 rows × 23 columns
Abgesehen von Klimaanlage entstehen None Values in den neu erzeugten Ausstattungsspalten wenn die Spalte Untertitel None ist, daher werden diese nun durch False ersetzt. Dafür wird davon ausgegangen, dass ein Fahrzeug bspw. keine Alufelgen hat, wenn keine Beschreibung unter Untertitel angegeben ist.
AutoDF['Alufelgen'] = AutoDF['Alufelgen'].replace(np.NaN, False)
AutoDF['Sitzheizung'] = AutoDF['Sitzheizung'].replace(np.NaN, False)
AutoDF['Einparkhilfe'] = AutoDF['Einparkhilfe'].replace(np.NaN, False)
AutoDF['Navigationssystem'] = AutoDF['Navigationssystem'].replace(np.NaN, False)
AutoDF
| Titel | Version | Untertitel | Preis | Leasing | km | Erstzulassung | PS | Zustand | Fahrzeughalter | ... | Emissionen_g_pro_km | Marke | Stadt | PLZ | Land | Alufelgen | Sitzheizung | Klimaanlage | Einparkhilfe | Navigationssystem | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Lincoln Town Car | Signature Series Prins Gasanlage Voll | NaN | 4900 | 0.0 | 277711 | 1993 | 213 | Gebraucht | 2 | ... | NaN | Lincoln | Berlin | 12107 | DE | False | False | False | False | False |
| 1 | Mercedes-Benz 200 | HECKFLOSSE W110 DAIMLER BENZ | NaN | 14870 | 0.0 | 39526 | 1965 | 95 | Gebraucht | NaN | ... | NaN | Mercedes-Benz | Berlin | 12305 | DE | False | False | False | False | False |
| 2 | Land Rover Discovery | 2.5 Base TDI | NaN | 3990 | 0.0 | 214446 | 1992 | 111 | Gebraucht | NaN | ... | NaN | Land | OLOT | 17800 | ES | False | False | False | False | False |
| 3 | MG MGB | GT aus 1. Hand, original 53.000 KM | NaN | 25900 | 0.0 | 53372 | 1975 | 133 | Gebraucht | 1 | ... | NaN | MG | Hamburg | 22297 | DE | False | False | False | False | False |
| 4 | Mercedes-Benz | 220 S Ponton W180 Oldtimer H-Kennz. fahrbereit | NaN | 22950 | 0.0 | 54900 | 1957 | 101 | Gebraucht | NaN | ... | NaN | Mercedes-Benz | Neumarkt | 92318 | DE | False | False | False | False | False |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 10268 | Skoda Superb | Combi 2.0TDI Active AHK Sitzheizung | Anhängerkupplung, Armlehne, Sitzheizung, Dachr... | 22840 | 0.0 | 56800 | 2019 | 150 | Gebraucht | 1 | ... | NaN | Skoda | Nürnberg | 90441 | DE | False | True | False | False | False |
| 10269 | BMW Z4 | sDrive20i Sport Steptronic Xenon Leder SHZ | Sportfahrwerk, Sportpaket, Sportsitze, Einpark... | 22990 | 0.0 | 85300 | 2012 | 184 | Gebraucht | 2 | ... | 161 | BMW | Ibbenbüren | 49477 | DE | False | True | False | True | False |
| 10270 | Audi A3 | 2,0 Cabriolet Ambition S-line+Sitzheizung+Tagf... | Mitsubishi & Honda Vertragspartner seit 40 Jahren | 14880 | 0.0 | 68335 | 2012 | 200 | Gebraucht | 3 | ... | 171 | Audi | Mönchengladbach | 41061 | DE | False | False | False | False | False |
| 10271 | Honda CR-V | 2,0 i-VTEC Comfort Leder+Allrad+Multif.Lenkrad... | Allrad, Zentralverriegelung, Lederlenkrad, Kat... | 14280 | 0.0 | 56105 | 2012 | 150 | Gebraucht | 1 | ... | 195 | Honda | Viersen | 41747 | DE | True | False | False | False | False |
| 10272 | Hyundai i30 | 1.6 Turbo GDI Turbo | Zentralverriegelung, Tempomat, Lichtsensor, Tr... | 13290 | 0.0 | 103882 | 2016 | 186 | Gebraucht | 2 | ... | 173 | Hyundai | Pfaffenhofen | 85276 | DE | False | True | True | False | False |
10273 rows × 23 columns
In der Spalte Leasing werden die Werte noch mit 0.0 für False und 1.0 für True ausgegeben. Dies wird in Boolean Werte geändert.
AutoDF['Leasing'] = AutoDF['Leasing'].replace(0.0, False)
AutoDF['Leasing'] = AutoDF['Leasing'].replace(1.0, True)
Mit der .info() Methode werden nun alle Spalten des Dataframes mit deren Datentypen angezeigt.
AutoDF.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10273 entries, 0 to 10272
Data columns (total 23 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Titel 10273 non-null object
1 Version 10273 non-null object
2 Untertitel 5613 non-null object
3 Preis 10273 non-null object
4 Leasing 10273 non-null bool
5 km 10273 non-null object
6 Erstzulassung 10273 non-null object
7 PS 9828 non-null object
8 Zustand 10273 non-null object
9 Fahrzeughalter 4983 non-null object
10 Getriebe 10273 non-null object
11 Kraftstoff 10273 non-null object
12 Verbrauch_l_pro_100km 5422 non-null object
13 Emissionen_g_pro_km 3770 non-null object
14 Marke 10273 non-null object
15 Stadt 10099 non-null object
16 PLZ 10099 non-null object
17 Land 10099 non-null object
18 Alufelgen 10273 non-null bool
19 Sitzheizung 10273 non-null bool
20 Klimaanlage 10273 non-null bool
21 Einparkhilfe 10273 non-null bool
22 Navigationssystem 10273 non-null bool
dtypes: bool(6), object(17)
memory usage: 1.4+ MB
Das Dataframe hat 22 Spalten. Davon haben die meisten den Datentyp object, obwohl es sich bei einigen davon um numerische Werte handelt. Dies muss noch geändert werden. Lediglich die Boolean Spalten wie beispielsweise Leasing wurden korrekt identifiziert.
Die meisten Spalten haben keine NULL Werte. Allerdings exisitieren auch Spalten, die sehr viele NULL-Werte aufweisen. Beispielsweise Emissionen_g_pro_km.
Nachfolgend werden die NULL-Werte in einer heatmap visuaisiert.
sns.set_theme(style="ticks", color_codes=True)
# Identifizieren der NULL Werte via Heatmap
sns.heatmap(AutoDF.isnull(),
yticklabels=False,
cbar=False,
cmap='viridis');
In der Heatmap ist zu erkennen, dass sehr viele NULL-Werte in den Spalten Untertitel, Fahrzeughalter, Verbrauch_l_pro_100km und Emissionen_g_pro_km exisitieren.
Nachfolgend werden hierfür nochmal die exakten Mengen ausgegeben:
print(AutoDF.isnull().sum())
Titel 0
Version 0
Untertitel 4660
Preis 0
Leasing 0
km 0
Erstzulassung 0
PS 445
Zustand 0
Fahrzeughalter 5290
Getriebe 0
Kraftstoff 0
Verbrauch_l_pro_100km 4851
Emissionen_g_pro_km 6503
Marke 0
Stadt 174
PLZ 174
Land 174
Alufelgen 0
Sitzheizung 0
Klimaanlage 0
Einparkhilfe 0
Navigationssystem 0
dtype: int64
Die Spalten Verbrauch_l_pro_100km und Emissionen_g_pro_km weisen viele NULL-Werte auf. Für die Analyse sind diese jedoch von großer Bedeutung. Zeilen mit fehlenden Abgas- oder Verbrauchswerten bzw. fehlenden Kilometer und PS-Angaben werden daher gelöscht.
noch mehr Begründung, warum wir Null Werte löschen?¶
AutoDF = AutoDF[AutoDF['Verbrauch_l_pro_100km'].notna()]
AutoDF = AutoDF[AutoDF['Emissionen_g_pro_km'].notna()]
AutoDF = AutoDF[AutoDF['km'].notna()]
AutoDF = AutoDF[AutoDF['PS'].notna()]
print(AutoDF.isnull().sum())
Titel 0
Version 0
Untertitel 636
Preis 0
Leasing 0
km 0
Erstzulassung 0
PS 0
Zustand 0
Fahrzeughalter 1034
Getriebe 0
Kraftstoff 0
Verbrauch_l_pro_100km 0
Emissionen_g_pro_km 0
Marke 0
Stadt 122
PLZ 122
Land 122
Alufelgen 0
Sitzheizung 0
Klimaanlage 0
Einparkhilfe 0
Navigationssystem 0
dtype: int64
Als nächstes werden die Datentypen angepasst, indem numerische Spalten einer Datentypkonvertierung unterzogen werden.
AutoDF['Preis'] = AutoDF['Preis'].astype('int')
AutoDF['km'] = AutoDF['km'].astype('int')
AutoDF['PS'] = AutoDF['PS'].astype('int')
AutoDF['Emissionen_g_pro_km'] = AutoDF['Emissionen_g_pro_km'].astype('int')
AutoDF['Erstzulassung'] = AutoDF['Erstzulassung'].astype('float')
AutoDF['Verbrauch_l_pro_100km'] = AutoDF['Verbrauch_l_pro_100km'].astype('float')
Die Spalten Zustand, Getriebe, Kraftstoff Marke und Land weisen jeweils nur eine geringe Menge verschiedener Ausprägungen vor. Daher werden diese Spalten im Typ categorical abgespeichert. Alle übrigen Spalten verbleiben als object.
AutoDF['Zustand'] = AutoDF['Zustand'].astype('category')
AutoDF['Getriebe'] = AutoDF['Getriebe'].astype('category')
AutoDF['Kraftstoff'] = AutoDF['Kraftstoff'].astype('category')
AutoDF['Marke'] = AutoDF['Marke'].astype('category')
AutoDF['Land'] = AutoDF['Land'].astype('category')
AutoDF.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 3337 entries, 21 to 10272
Data columns (total 23 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Titel 3337 non-null object
1 Version 3337 non-null object
2 Untertitel 2701 non-null object
3 Preis 3337 non-null int32
4 Leasing 3337 non-null bool
5 km 3337 non-null int32
6 Erstzulassung 3337 non-null float64
7 PS 3337 non-null int32
8 Zustand 3337 non-null category
9 Fahrzeughalter 2303 non-null object
10 Getriebe 3337 non-null category
11 Kraftstoff 3337 non-null category
12 Verbrauch_l_pro_100km 3337 non-null float64
13 Emissionen_g_pro_km 3337 non-null int32
14 Marke 3337 non-null category
15 Stadt 3215 non-null object
16 PLZ 3215 non-null object
17 Land 3215 non-null category
18 Alufelgen 3337 non-null bool
19 Sitzheizung 3337 non-null bool
20 Klimaanlage 3337 non-null bool
21 Einparkhilfe 3337 non-null bool
22 Navigationssystem 3337 non-null bool
dtypes: bool(6), category(5), float64(2), int32(4), object(6)
memory usage: 325.1+ KB
Descriptive Statistics¶
Nachfolgend werden alle numerischen Features in einer Liste gespeichert.
num_features=AutoDF.select_dtypes(include=np.number).columns.to_list()
num_features
['Preis',
'km',
'Erstzulassung',
'PS',
'Verbrauch_l_pro_100km',
'Emissionen_g_pro_km']
Gleiches wird für alle nicht numerischen Features durchgeführt.
cat_features=AutoDF.select_dtypes(exclude=np.number).columns.to_list()
cat_features
['Titel',
'Version',
'Untertitel',
'Leasing',
'Zustand',
'Fahrzeughalter',
'Getriebe',
'Kraftstoff',
'Marke',
'Stadt',
'PLZ',
'Land',
'Alufelgen',
'Sitzheizung',
'Klimaanlage',
'Einparkhilfe',
'Navigationssystem']
Für einen ersten Überblick über die Datenverteilung numerischer Features bietet sich die describe() Methode an. Diese gibt für jede Spalte die Anzahl, Durchschnitt, Standardabweichung, Minimum, Maximum sowie die Quartile an.
AutoDF.describe().transpose()
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| Preis | 3337.0 | 25348.575667 | 25826.283777 | 219.0 | 11330.0 | 18890.0 | 28950.0 | 239990.0 |
| km | 3337.0 | 99516.753671 | 63420.382807 | 900.0 | 50704.0 | 86835.0 | 137087.0 | 425000.0 |
| Erstzulassung | 3337.0 | 2008.643093 | 9.119443 | 1950.0 | 2004.0 | 2011.0 | 2015.0 | 2021.0 |
| PS | 3337.0 | 224.735091 | 115.791800 | 18.0 | 140.0 | 190.0 | 290.0 | 706.0 |
| Verbrauch_l_pro_100km | 3337.0 | 7.994996 | 2.912554 | 0.0 | 6.0 | 7.3 | 9.4 | 21.1 |
| Emissionen_g_pro_km | 3337.0 | 193.681450 | 69.525198 | 0.0 | 147.0 | 178.0 | 225.0 | 488.0 |
Alle Features haben einen Count von 3337, da dies der Anzahl der Datenpunkt entspricht.
Werte ändern sich hier jetzt mit jeder Ausführung. Müssen irgendwann einen Stand safen und den diskutieren¶
Nachfolgend wird die Anzahl der unique values je Feature ausgegeben.
for col in AutoDF.columns:
values = AutoDF[col].unique()
print(col, "has", len(AutoDF[col].unique()), "unique values")
Titel has 300 unique values
Version has 683 unique values
Untertitel has 568 unique values
Preis has 488 unique values
Leasing has 2 unique values
km has 606 unique values
Erstzulassung has 35 unique values
PS has 160 unique values
Zustand has 1 unique values
Fahrzeughalter has 9 unique values
Getriebe has 3 unique values
Kraftstoff has 6 unique values
Verbrauch_l_pro_100km has 108 unique values
Emissionen_g_pro_km has 172 unique values
Marke has 41 unique values
Stadt has 228 unique values
PLZ has 268 unique values
Land has 20 unique values
Alufelgen has 2 unique values
Sitzheizung has 2 unique values
Klimaanlage has 2 unique values
Einparkhilfe has 2 unique values
Navigationssystem has 2 unique values
Beschreiben, was wir da sehen
Als nächstes wird die Anzahl der Fahrzeuge pro Kraftstoffart ausgegeben.
print(AutoDF['Kraftstoff'].value_counts())
Benzin 2425
Diesel 850
Elektro/Benzin 42
Autogas (LPG) 14
Elektro/Diesel 4
Elektro 2
Name: Kraftstoff, dtype: int64
Die häufigste Kraftstoffart mit xxx Fahrzeugen ist Benzin, gefolgt von Diesel.
Alle anderen Kraftstoffarten kommen in Relation zur Gesamtmenge an Fahrzeugen eher selten vor.
Ebenso interessant ist die Anzahl der Fahrzeuge pro Getriebeart.
print(AutoDF['Getriebe'].value_counts())
Automatik 1772
Schaltgetriebe 1530
Halbautomatik 35
Name: Getriebe, dtype: int64
Automatik und Schaltgetriebe kommen ungefähr gleich oft vor. Halbautomatikfahrzeuge kommen dagegen eher selten vor.
Nachfolgend wird der prozentuale Anteil jeder Automarke in Bezug auf die Gesamtmasse aller Fahrzeuge ausgegeben.
print(AutoDF['Marke'].value_counts(normalize=True))
Mercedes-Benz 0.166916
BMW 0.166317
Audi 0.165418
Volkswagen 0.118070
Porsche 0.061432
Opel 0.046149
Ford 0.036260
Jaguar 0.031166
Volvo 0.019778
Renault 0.019179
MINI 0.018580
Citroen 0.013785
Fiat 0.012286
Alfa 0.009589
Skoda 0.009290
Honda 0.008990
Toyota 0.008990
Aston 0.008391
Hyundai 0.008391
Kia 0.008091
SEAT 0.008091
Mitsubishi 0.006293
Mazda 0.005993
SsangYong 0.004795
Sonstige 0.004795
Saab 0.004795
Dodge 0.004795
Nissan 0.003596
Peugeot 0.003296
MG 0.002397
Alpina 0.002397
Suzuki 0.002098
Cadillac 0.002098
Lexus 0.001798
Cupra 0.001498
Ferrari 0.001199
Subaru 0.000899
Chevrolet 0.000899
smart 0.000599
Land 0.000300
Jeep 0.000300
Name: Marke, dtype: float64
Die häufigste auf Autoscout24 angebotene Automarke ist Mercedes-Benz (16,69%), dicht gefolgt von von BMW (16,63%) und Audi (16,54%).
Die drei seltensten Automarken sind Smart (0,05%), Land (0,03%) und Jeep (0,03%).
AutoDF.hist(bins=20, figsize=(20,15))
plt.show()
fig = px.histogram(AutoDF, x="Preis",title="Distribution over price (Euro)")
pyo.iplot(fig)
Untersuchung der numerischen Variablen¶
Um eine erste Übersicht über mögliche Zusammenhänge zwischen den verschiedenen Variablen zu erhalten, wird zunächst ein Pairplot erstellt. Um dn Plot übersichtlich zu halten, werden Variablen ausgewählt, zwischen denen bereits Korrelation vermutet wird.
sns.pairplot(data=AutoDF, vars=["Preis","PS","km","Verbrauch_l_pro_100km","Emissionen_g_pro_km"], hue="Kraftstoff",)
<seaborn.axisgrid.PairGrid at 0x28d1829ebe0>
sns.lmplot(data=AutoDF, x='PS', y='Preis')
<seaborn.axisgrid.FacetGrid at 0x2217511eee0>
Tatsächlich können im Pairplot Zusammenhänge zwischen einzelnen Variablen erkannt werden. Vor allem die starke Korrelation zwischen Verbrauch und Emissionen fällt im Plot auf, ist allerdings selbstverständlich da mit höherem Verbrauch in der Regel auch mehr Emissionen erzeugt werden. Doch auch weniger starke Abhängigkeiten von bspw. PS auf Preis können erkannt werden.
Nach der optischen Darstellung sollen nun im nächsten Schritt die Abhängigkeiten noch einmal in Zahlen dargestellt werden.
corr = AutoDF.corr()
corr['Preis'].sort_values(ascending=False)
Preis 1.000000
PS 0.638727
Emissionen_g_pro_km 0.417156
Verbrauch_l_pro_100km 0.414372
Erstzulassung 0.063967
Einparkhilfe -0.035926
Leasing -0.058178
Navigationssystem -0.063409
Sitzheizung -0.084809
Klimaanlage -0.114547
Alufelgen -0.150819
km -0.339976
Name: Preis, dtype: float64
# Create correlation matrix for numerical variables
corr_matrix = AutoDF.corr()
corr_matrix
| Preis | Leasing | km | Erstzulassung | PS | Verbrauch_l_pro_100km | Emissionen_g_pro_km | Alufelgen | Sitzheizung | Klimaanlage | Einparkhilfe | Navigationssystem | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Preis | 1.000000 | -0.058178 | -0.339976 | 0.063967 | 0.638727 | 0.414372 | 0.417156 | -0.150819 | -0.084809 | -0.114547 | -0.035926 | -0.063409 |
| Leasing | -0.058178 | 1.000000 | -0.025905 | 0.066041 | -0.040683 | -0.059894 | -0.064324 | -0.005368 | 0.055004 | 0.102760 | -0.000073 | 0.018808 |
| km | -0.339976 | -0.025905 | 1.000000 | -0.158512 | -0.069001 | 0.092129 | 0.124874 | 0.073435 | -0.011293 | 0.005921 | -0.074650 | -0.018828 |
| Erstzulassung | 0.063967 | 0.066041 | -0.158512 | 1.000000 | 0.190821 | -0.269409 | -0.254416 | 0.025535 | 0.206469 | 0.127959 | 0.272622 | 0.271829 |
| PS | 0.638727 | -0.040683 | -0.069001 | 0.190821 | 1.000000 | 0.652536 | 0.644422 | -0.164769 | -0.020136 | -0.134800 | 0.023753 | 0.061894 |
| Verbrauch_l_pro_100km | 0.414372 | -0.059894 | 0.092129 | -0.269409 | 0.652536 | 1.000000 | 0.984834 | -0.038358 | -0.121253 | -0.118479 | -0.175432 | -0.136112 |
| Emissionen_g_pro_km | 0.417156 | -0.064324 | 0.124874 | -0.254416 | 0.644422 | 0.984834 | 1.000000 | -0.035263 | -0.120236 | -0.111865 | -0.174386 | -0.133477 |
| Alufelgen | -0.150819 | -0.005368 | 0.073435 | 0.025535 | -0.164769 | -0.038358 | -0.035263 | 1.000000 | 0.076953 | 0.181066 | 0.071659 | 0.090605 |
| Sitzheizung | -0.084809 | 0.055004 | -0.011293 | 0.206469 | -0.020136 | -0.121253 | -0.120236 | 0.076953 | 1.000000 | 0.062076 | 0.149010 | 0.272942 |
| Klimaanlage | -0.114547 | 0.102760 | 0.005921 | 0.127959 | -0.134800 | -0.118479 | -0.111865 | 0.181066 | 0.062076 | 1.000000 | 0.022308 | 0.044652 |
| Einparkhilfe | -0.035926 | -0.000073 | -0.074650 | 0.272622 | 0.023753 | -0.175432 | -0.174386 | 0.071659 | 0.149010 | 0.022308 | 1.000000 | 0.253472 |
| Navigationssystem | -0.063409 | 0.018808 | -0.018828 | 0.271829 | 0.061894 | -0.136112 | -0.133477 | 0.090605 | 0.272942 | 0.044652 | 0.253472 | 1.000000 |
# Erstellen einer Heatmap um Abhängigkeiten zwischen den verschiedenen Variablen zu visualisieren
# Use a mask to plot only part of a matrix
mask = np.zeros_like(corr_matrix)
mask[np.triu_indices_from(mask)]= True
# Erstellen der Heatmap mit zusätzlichen Parametern
plt.subplots(figsize=(11, 15))
heatmap = sns.heatmap(corr_matrix,
mask = mask,
square = True,
linewidths = .5,
cmap = 'coolwarm',
cbar_kws = {'shrink': .6,
'ticks' : [-1, -.5, 0, 0.5, 1]},
vmin = -1,
vmax = 1,
annot = True,
annot_kws = {"size": 10})
sns.boxenplot(data=AutoDF, x="Preis", y="Marke", orient="h", width=2)
<AxesSubplot:xlabel='Preis', ylabel='Marke'>
was macht sorted_nob ????
sorted_nb = AutoDF.groupby(['Marke'])['Preis'].median().sort_values()
sorted_nb
Marke
smart 5470.0
Fiat 5500.0
Suzuki 5890.0
Toyota 9990.0
Honda 10240.0
Nissan 10590.0
SsangYong 10980.0
MINI 11850.0
Opel 11900.0
Subaru 11990.0
Alfa 12880.0
Mitsubishi 12890.0
Cadillac 12990.0
Renault 13880.0
BMW 14990.0
Saab 14990.0
Volvo 15300.0
Mazda 15990.0
Volkswagen 16490.0
Lexus 16990.0
Ford 18890.0
SEAT 19330.0
Citroen 19970.0
Peugeot 19990.0
Kia 20990.0
Audi 21990.0
Hyundai 22980.0
Skoda 22990.0
Mercedes-Benz 23400.0
Jeep 23990.0
Cupra 24499.0
MG 27950.0
Jaguar 39980.0
Chevrolet 40990.0
Sonstige 45899.0
Porsche 48990.0
Land 49999.0
Alpina 52990.0
Dodge 55800.0
Aston 119900.0
Ferrari 139945.0
Name: Preis, dtype: float64
sns.boxplot(x=AutoDF['Marke'], y=AutoDF['Preis'], order=list(sorted_nb.index))
<AxesSubplot:xlabel='Marke', ylabel='Preis'>
Untersuchung der kategorialen Features¶
Untersuchung der Features “Ausstattung”¶
Als nächstes soll untersucht werden, inwiefern die Ausstattungsmerkmal einen eindeutigen Einfluss auf den Preis haben.
Austtattung=['Klimaanlage','Alufelgen','Sitzheizung','Einparkhilfe','Navigationssystem']
Austtattung
['Klimaanlage',
'Alufelgen',
'Sitzheizung',
'Einparkhilfe',
'Navigationssystem']
fig, ax = plt.subplots(2, 3, figsize=(15, 10))
for var, subplot in zip(Austtattung, ax.flatten()):
sns.boxplot(x=var, y='Preis', data=AutoDF, ax=subplot)
Fazit: Keine`??
Untersuchung der Features Kraftstoff, Getriebe¶
PriceAveragePerKraftstoff=AutoDF.groupby(by="Kraftstoff")["Preis"].mean()
PriceAveragePerKraftstoff.plot(kind="bar",figsize=(12,6),color="m",title="Average Price per Kraftstoff")
<AxesSubplot:title={'center':'Average Price per Kraftstoff'}, xlabel='Kraftstoff'>
sns.stripplot(data=AutoDF, x="Kraftstoff", y="Preis" , size=3 )
<AxesSubplot:xlabel='Kraftstoff', ylabel='Preis'>
Diesel=AutoDF[AutoDF["Kraftstoff"]=="Diesel"]
Benzin=AutoDF[AutoDF["Kraftstoff"]=="Benzin"]
Elektro=AutoDF[AutoDF["Kraftstoff"]=="Elektro"]
Diesel
| car | version | subtitle | price | leasing | km | Erstzulassung | PS | Zustand | Fahrzeughalter | ... | Emissionen_g_pro_km | Marke | city | PLZ | country | Alufelgen | Sitzheizung | Klimaanlage | Einparkhilfe | Navigationssystem | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 846 | Mitsubishi Pajero | 2800 TD GLS Panorama+7 Sitzer+long+AHK | NaN | 12890 | 0.0 | 202000 | 1994.0 | 125 | Gebraucht | 1 | ... | 297 | Mitsubishi | Duisburg | 47249 | DE | False | False | False | False | False |
| 1008 | Volkswagen T4 | Transporter Pritsche Doppelkabine 1.9 D *PLANE... | NaN | 3499 | 0.0 | 215983 | 1994.0 | 60 | Gebraucht | 2 | ... | 198 | Volkswagen | Ilm | 85276 | DE | False | False | False | False | False |
| 1140 | Volkswagen T4 Multivan | 2.5TDI Allstar | NaN | 9999 | 0.0 | 425000 | 1996.0 | 102 | Gebraucht | 3 | ... | 232 | Volkswagen | Weißenhorn | 89264 | DE | False | False | False | False | False |
| 1237 | Mitsubishi Pajero | 2800 TD GLS Panorama+7 Sitzer+long+AHK | NaN | 12890 | 0.0 | 202000 | 1994.0 | 125 | Gebraucht | 1 | ... | 297 | Mitsubishi | Duisburg | 47249 | DE | False | False | False | False | False |
| 1424 | Volkswagen T4 | Transporter Pritsche Doppelkabine 1.9 D *PLANE... | NaN | 3499 | 0.0 | 215983 | 1994.0 | 60 | Gebraucht | 2 | ... | 198 | Volkswagen | Ilm | 85276 | DE | False | False | False | False | False |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 6063 | Volkswagen Caddy | 2.0 TDI DSG 4Motion Xenon*Standh*ACC*Navi | Allrad, Schiebetür rechts, Navigationssystem, ... | 24990 | 0.0 | 112703 | 2016.0 | 150 | Gebraucht | 1 | ... | 146 | Volkswagen | Dresden | 01139 | DE | True | True | False | False | True |
| 6067 | Audi A6 | 3.0 TDI competition quattro Navi/Leder/Temp/ | Sitzheizung, Allrad, Sportpaket, Zentralverrie... | 35490 | 0.0 | 112000 | 2017.0 | 326 | Gebraucht | NaN | ... | 164 | Audi | Braunschweig | 38110 | DE | False | True | True | True | False |
| 6069 | Audi A3 | Sportback Sport 2.0 TDI quattro S-tronic Klima | Sportsitze, Multifunktionslenkrad, Kopfairbag,... | 18980 | 0.0 | 131800 | 2016.0 | 184 | Gebraucht | 1 | ... | 133 | Audi | Reichenhall | 83435 | DE | False | False | False | True | False |
| 6076 | Skoda Superb | Combi 2.0 TDI Ambition AHK Navi | Anhängerkupplung, Navigationssystem, Dachrelin... | 23890 | 0.0 | 48658 | 2019.0 | 150 | Gebraucht | 1 | ... | 114 | Skoda | Stuttgart | 70565 | DE | True | False | False | True | True |
| 6080 | Skoda Kamiq | 1.6TDI DSG Ambition*Vill/LED*SmartLink*17Zoll* | Sitzheizung, Schlüssellose Zentralverriegelung... | 24950 | 0.0 | 35000 | 2020.0 | 116 | Gebraucht | 1 | ... | 112 | Skoda | found | NaN | True | True | False | True | False |
409 rows × 23 columns
npDiesel = Diesel["Preis"]
npBenzin = Benzin["Preis"]
#npElektro = Elektro["price"]
data = [npDiesel.values, npBenzin.values]
#data = [npDiesel.values, npBenzin.values, npElektro.values]
group_labels = ['Diesel', 'Benzin']
#group_labels = ['Diesel', 'Benzin', 'Elektro']
colors = ['#462EDE', '#DE2EBE', '#FF8033']
# Create distplot with curve_type set to 'normal'
fig = ff.create_distplot(data, group_labels, colors=colors,
bin_size=3000, show_rug=False)
# Add title
fig.update_layout(title_text='Hist and Curve Plot')
pyo.iplot(fig)
fig=px.scatter(AutoDF,x="PS",y="Preis",color="Emissionen_g_pro_km",size="Verbrauch_l_pro_100km",
hover_data=["Marke","Titel","Kraftstoff"],title="Price over PS",
trendline="ols")
pyo.iplot(fig)
Kartenvisualisierung¶
Als nächstes werden wird der Standort der zum Verkauf angebotenen Fahrzeuge in einer Landkarte visualisiert.
Für diese Zwecke wurde beim Webcrawling das Attribut Location von Autoscout24 abgezogen und anschließend in der Datenaufbereitung der Stadtname und das Land als extra Spalte angelegt.
Mithilfe des Geolocators werden jeder Stadt Longitude und Latitude zugeordnet und im Dataframe geoDF gespeichert. Dieses Dataframe wird anschließend über den Index mit dem AutoDF gejoined.
geolocator = Nominatim(user_agent="my_app")
geoDF = pd.DataFrame()
for city in AutoDF.index:
try:
location = geolocator.geocode(AutoDF['Stadt'][city])
geoDF = geoDF.append({"longitude": location.longitude, "latitude": location.latitude}, ignore_index=True)
except:
geoDF = geoDF.append({"longitude": None, "latitude": None}, ignore_index=True)
geoDF
#Index von AutoDF zurücksetzen, da aufgrund der Entfernung der Null-Values bei Verbrauch und Emissionen viele Zeilen weggefallen sind
#sonst kann nicht mit GeoDF gejoined werden
AutoDF = AutoDF.reset_index(drop=True)
#Join von GeoDF und AutoDF über Index
AutoDF = pd.merge(AutoDF, geoDF, left_index=True, right_index=True)
Unter Verwendung von Longitude und Latitude werden die Fahrzeugstandorte auf einer Folium Map visualisiert.
Zusätzlich wird jedem Datenpunkt eine Pop-Up Beschreibung hingefügt, welche Stadtname, Autobeschreibung und Preis beinhaltet.
AutoDF
| car | version | subtitle | price | leasing | location | km | Erstzulassung | PS | Zustand | ... | city | PLZ | country | Alufelgen | Sitzheizung | Klimaanlage | Einparkhilfe | Navigationssystem | longitude | latitude | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Citroen 2CV | 2 CV 6 0,6 Club Cabrio | NaN | 19970 | 0.0 | Verkaufsteam Neuensalz • DE-08541 Neuensalz be... | 50704 | 1987 | 27 | Gebraucht | ... | Plauen | 08541 | DE | False | False | False | False | False | 12.134652 | 50.495063 |
| 1 | Citroen 2CV | 6 0,6 Club Cabrio 20 kW (27 PS), Schaltgetriebe | NaN | 22570 | 0.0 | David Meßmer • DE-85614 Kirchseon | 50704 | 1987 | 27 | Gebraucht | ... | Kirchseon | 85614 | DE | False | False | False | False | False | NaN | NaN |
| 2 | Sonstige Marken | ANDERE RMC Roadster 1950 Linkslenker | NaN | 45899 | 0.0 | Andreas Astaller • DE-84069 Schierling | 40854 | 1950 | 101 | Gebraucht | ... | Schierling | 84069 | DE | False | False | False | False | False | 12.137863 | 48.835088 |
| 3 | Ford Fiesta | Sammlerfahrzeug top gepflegt | NaN | 9990 | 0.0 | Armin Jungkeit • DE-79108 Freiburg | 47898 | 1980 | 53 | Gebraucht | ... | Freiburg | 79108 | DE | False | False | False | False | False | 7.849400 | 47.996090 |
| 4 | BMW 316 | BAUR Cabrio mit geringer Laufleistung | NaN | 11980 | 0.0 | John Klass • DE-58285 Gevelsberg | 129301 | 1987 | 90 | Gebraucht | ... | Gevelsberg | 58285 | DE | False | False | False | False | False | 7.340479 | 51.320742 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1668 | Audi RS6 | PERFORMANCE V-MAX OFFEN MATT 360*CAM | NaN | 66399 | 0.0 | Herr Beltekoglu • DE-73084 Salach | 90000 | 2017 | 605 | Gebraucht | ... | Salach | 73084 | DE | False | False | False | False | False | 9.739514 | 48.690408 |
| 1669 | Skoda Kamiq | 1.6TDI DSG Ambition*Vill/LED*SmartLink*17Zoll* | Sitzheizung, Schlüssellose Zentralverriegelung... | 24950 | 0.0 | not found | 35000 | 2020 | 116 | Gebraucht | ... | found | NaN | True | True | False | True | False | -122.384327 | 47.667539 | |
| 1670 | Jaguar F-Type | P380 S AWD Leder Navi Kamera BR-Green | Elektrische Heckklappe, Wegfahrsperre, Einpark... | 58925 | 0.0 | Bernd Wagner • DE-15517 Fürstenwalde/Spree | 26500 | 2016 | 381 | Gebraucht | ... | Fürstenwalde/Spree | 15517 | DE | False | False | False | True | False | 14.064985 | 52.358021 |
| 1671 | BMW 125 | i Advantage/17"/Klima/PDC/Sitzheiz | Sportfahrwerk, Sitzheizung, Zentralverriegelun... | 17590 | 0.0 | Ihr Hermann Menton Verlkausteam • DE-72072 Tüb... | 83422 | 2015 | 218 | Gebraucht | ... | Tübingen | 72072 | DE | False | True | True | False | False | 9.053553 | 48.523616 |
| 1672 | Mercedes-Benz A 250 | Progressive LED Navi Licht&Sicht | Klimaautomatik, LED-Scheinwerfer, Navigationss... | 25990 | 0.0 | Ihr Rosier Team • DE-38122 Braunschweig | 95768 | 2018 | 224 | Gebraucht | ... | Braunschweig | 38122 | DE | False | False | True | False | True | 10.523607 | 52.264658 |
1673 rows × 26 columns
m = folium.Map([50.0 , 10.0],zoom_start=4)
for i in AutoDF.index:
try:
folium.Marker( location=[ AutoDF['latitude'][i], AutoDF['longitude'][i] ], popup = [AutoDF['Stadt'][i], AutoDF['Titel'][i], AutoDF['Preis'][i]]).add_to(m)
except:
Continue
m